#include "object_pool.hpp"
#include <cassert>
#include <iostream>
#include <vector>
#ifdef _WIN32
    #include <windows.h>
#endif // _WIN32

struct MyObj
{
    uint64_t a = 0;
};

void Test()
{
    ObjectPool<MyObj> pool(8);
    std::atomic<uint64_t> k(0);
    uint64_t sum1 = 0;
    uint64_t sum2 = 0;
    const uint32_t OBJECT_NUM = pool.GetSize();
    {
        std::shared_ptr<MyObj>* object_ptr_array = new std::shared_ptr<MyObj>[OBJECT_NUM];
        for (uint32_t i = 0; i < OBJECT_NUM; ++i)
        {
            object_ptr_array[i] = pool.AcquireObject();
            assert(object_ptr_array[i] != nullptr);
            object_ptr_array[i]->a = i;
            sum1 = sum1 + object_ptr_array[i]->a;
        }
        assert(pool.AcquireObject() == nullptr);
        delete[] object_ptr_array;
    }

    {
        std::shared_ptr<MyObj>* object_ptr_array = new std::shared_ptr<MyObj>[OBJECT_NUM];
        for (uint32_t i = 0; i < OBJECT_NUM; ++i)
        {
            object_ptr_array[i] = pool.AcquireObject();
            assert(object_ptr_array[i] != nullptr);
            sum2 = sum2 + object_ptr_array[i]->a;
        }
        assert(pool.AcquireObject() == nullptr);
        assert(sum1 == sum2);
        delete[] object_ptr_array;
    }

    std::vector<std::thread> threads;
    for (int i = 0; i < 4; ++i)
    {
        threads.emplace_back([&pool, &k] {
            for (uint32_t n = 0; n < 2000000; ++n)
            {
                const std::shared_ptr<MyObj> p1 = pool.AcquireObject();
                if (p1)
                {
                    assert(p1.use_count() == 1);
                    p1->a++;
                    ++k;
                }
            }
        });
    }

    for (auto &thread : threads)
    {
        if (thread.joinable())
        {
            thread.join();
        }
    }

    {
        uint64_t sum3 = 0;
        std::shared_ptr<MyObj>* object_ptr_array = new std::shared_ptr<MyObj>[OBJECT_NUM];
        for (uint32_t i = 0; i < OBJECT_NUM; ++i)
        {
            object_ptr_array[i] = pool.AcquireObject();
            assert(object_ptr_array[i] != nullptr);
            sum3 = sum3 + object_ptr_array[i]->a;
        }
        assert(pool.AcquireObject() == nullptr);
        assert(sum3 == sum2 + k);
        delete[] object_ptr_array;
    }
}

int main()
{
#ifdef _WIN32
    SetConsoleCP(65001);
    SetConsoleOutputCP(65001);
#endif // _WIN32
    Test();
    return 0;
}